如果你学习过我们之前在1.12.2的建筑生成教程,那么对本次的教程的理解可能会相对轻松。
往期回顾
Minecraft 1.12.2模组开发(十四) 建筑生成 (structure generation) Minecraft 1.12.2模组开发(二十二) 多种建筑生成
我们本次将在1.16.5版本中实现建筑的自动生成。
1.首先我们需要生成一个建筑的.nbt文件
创建一个新世界
通过命令让我们获得一个建筑方块:
/give @s minecraft:structure_block
![cr0.png](https://img-blog.csdnimg.cn/img_convert/a250c2fae5d63cf45723faa385260f8e.png)
之后我们建造一个建筑
![cr1.png](https://img-blog.csdnimg.cn/img_convert/d74fa6edf3aec9f6ed82b08f3c84d1df.png)
通过对建筑操作的坐标变换,将我们这个建筑完全包裹在区域内:
![cr2.png](https://img-blog.csdnimg.cn/img_convert/25105e051b1ac5c45951e31c5ebf831b.png)
点击’Save’,建筑成功保存下来
![cr3.png](https://img-blog.csdnimg.cn/img_convert/3cb399d7f0b38a3cfef1a644b081e8e3.png)
保存的建筑会生成一个.nbt文件,我们需要将这个文件找到,路径为
开发包\run\saves\1_8version_test(你的存档名称)\generated\minecraft\structures
2.在data包中新建structures文件夹,将生成的.nbt文件放入其中。
![cr4.png](https://img-blog.csdnimg.cn/img_convert/6b6696efef1bc983a020c21229ef58bb.png)
3.在worldgen文件夹中新建template_pool(建筑模板库)文件夹 -> 新建一个与我们建筑名称对应的文件夹(以tank1为例) -> 在tank1中新建一个start_pool.json文件
在start_pool.json中编写:
{
"name": "re8joymod:tank1/start_pool", //格式:模组名:建筑名称/start_pool的文件路径
"fallback": "minecraft:empty",
"elements": [
{
"weight": 1,
"element": {
"location": "re8joymod:tank1", //格式:模组名:建筑名称
"processors": "minecraft:empty",
"projection": "rigid",
"element_type": "minecraft:single_pool_element"
}
}
]
}
4.在common文件夹下新建 world文件夹 -> world文件夹中新建structure文件夹 -> structure文件夹中新建 structures文件夹 -> structures中新建一个建筑物类(以Tank1Structure.java为例)
![cr5.png](https://img-blog.csdnimg.cn/img_convert/950c91e6a03f221fa415013801efc8ed.png)
在Tank1Structure.java中编写:
package com.joy187.re8joymod.common.world.structure.structures;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Codec;
import net.minecraft.entity.EntityType;
import net.minecraft.util.SharedSeedRandom;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.MutableBoundingBox;
import net.minecraft.util.math.vector.Vector3i;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.MobSpawnInfo;
import net.minecraft.world.biome.provider.BiomeProvider;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.feature.jigsaw.JigsawManager;
import net.minecraft.world.gen.feature.structure.*;
import net.minecraft.util.registry.DynamicRegistries;
import net.minecraft.world.gen.feature.template.TemplateManager;
import net.minecraft.block.BlockState;
import net.minecraft.util.ResourceLocation;
import com.joy187.re8joymod.Utils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import java.util.List;
public class Tank1Structure extends Structure {
public Tank1Structure(Codec codec) {
super(codec);
}
@Override
public IStartFactory getStartFactory() {
return Tank1Structure.Start::new;
}
@Override
public GenerationStage.Decoration step() {
return GenerationStage.Decoration.SURFACE_STRUCTURES;
}
private static final List STRUCTURE_MONSTERS = ImmutableList.of(
new MobSpawnInfo.Spawners(EntityType.ILLUSIONER, 10, 1, 4),
new MobSpawnInfo.Spawners(EntityType.VINDICATOR, 10, 1, 4)
);
private static final List STRUCTURE_CREATURES = ImmutableList.of(
new MobSpawnInfo.Spawners(EntityType.SHEEP, 10, 1, 5),
new MobSpawnInfo.Spawners(EntityType.RABBIT, 10, 1, 4)
);
@Override
public List getDefaultSpawnList() {
return STRUCTURE_MONSTERS;
}
@Override
public List getDefaultCreatureSpawnList() {
return STRUCTURE_CREATURES;
}
@Override
protected boolean isFeatureChunk(ChunkGenerator chunkGenerator, BiomeProvider biomeSource,
long seed, SharedSeedRandom chunkRandom, int chunkX, int chunkZ,
Biome biome, ChunkPos chunkPos, NoFeatureConfig featureConfig) {
BlockPos centerOfChunk = new BlockPos((chunkX piece.move(0, 1, 0));
this.pieces.forEach(piece -> piece.getBoundingBox().y0 -= 1);
// Since by default, the start piece of a structure spawns with it's corner at centerPos
// and will randomly rotate around that corner, we will center the piece on centerPos instead.
// This is so that our structure's start piece is now centered on the water check done in isFeatureChunk.
// Whatever the offset done to center the start piece, that offset is applied to all other pieces
// so the entire structure is shifted properly to the new spot.
Vector3i structureCenter = this.pieces.get(0).getBoundingBox().getCenter();
int xOffset = centerPos.getX() - structureCenter.getX();
int zOffset = centerPos.getZ() - structureCenter.getZ();
for(StructurePiece structurePiece : this.pieces){
structurePiece.move(xOffset, 0, zOffset);
}
this.calculateBoundingBox();
//用作Debug,查看建筑生成的位置
LogManager.getLogger().log(Level.DEBUG, "Rundown House at " +
this.pieces.get(0).getBoundingBox().x0 + " " +
this.pieces.get(0).getBoundingBox().y0 + " " +
this.pieces.get(0).getBoundingBox().z0);
}
}
}
5.在刚刚新建的 structure文件夹中新建 ModStructure.java
在ModStructure.java中编写:
package com.joy187.re8joymod.common.world.structure;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.joy187.re8joymod.Utils;
import com.joy187.re8joymod.common.world.structure.structures.Tank1Structure;
import net.minecraft.util.registry.WorldGenRegistries;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.feature.structure.Structure;
import net.minecraft.world.gen.settings.DimensionStructuresSettings;
import net.minecraft.world.gen.settings.StructureSeparationSettings;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.HashMap;
import java.util.Map;
public class ModStructures {
// STStructures.java
public static final DeferredRegister> void setupMapSpacingAndLand(
F structure,
StructureSeparationSettings structureSeparationSettings,
boolean transformSurroundingLand)
{
/*
* We need to add our structures into the map in Structure class
* alongside vanilla structures or else it will cause errors.
*
* If the registration is setup properly for the structure,
* getRegistryName() should never return null.
*/
Structure.STRUCTURES_REGISTRY.put(structure.getRegistryName().toString(), structure);
/*
* Whether surrounding land will be modified automatically to conform to the bottom of the structure.
* Basically, it adds land at the base of the structure like it does for Villages and Outposts.
* Doesn't work well on structure that have pieces stacked vertically or change in heights.
*
* Note: The air space this method will create will be filled with water if the structure is below sealevel.
* This means this is best for structure above sealevel so keep that in mind.
*
* NOISE_AFFECTING_FEATURES requires AccessTransformer (See resources/META-INF/accesstransformer.cfg)
*/
if(transformSurroundingLand){
Structure.NOISE_AFFECTING_FEATURES = ImmutableList. |